/*
 * @Author: Lee 497232807@qq.com
 * @Date: 2023-08-03 08:47:41
 * @LastEditors: Lee 497232807@qq.com
 * @LastEditTime: 2023-08-07 09:56:45
 * @FilePath: \cocos_framework_base\extensions\publish_res\src\panels\default\index.ts
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
import { readFileSync } from 'fs-extra';
import path, { join } from 'path';
import fs from 'fs';
import { createApp, App, reactive, ref } from 'vue';
import { ConfigManager } from './configManager';
import { myUtils } from './utils';
import electron from 'electron';
import ChildProcess from 'child_process';

const panelDataMap = new WeakMap<any, App>();
/**
 * @zh 如果希望兼容 3.3 之前的版本可以使用下方的代码
 * @en You can add the code below if you want compatibility with versions prior to 3.3
 */
// Editor.Panel.define = Editor.Panel.define || function(options: any) { return options }

/**
 * 主包更新配置文件
 */
type remoteConfig = {
    scriptVersion: string,
    baseUrl: string,
    configFile: string,
    channels: string[],
    supportBinarys: string[],
    forcedBinaryVersions: string[],
}
/**
 * 子游戏更新配置文件
 */
type subGameConfig = {
    baseUrl: string,
    subgames: { [key: string]: { ID: number, version: string } }
}

/**
 * 版本文件配置
 */
type verisonConfig = {
    scriptVersion: string;
    files: {
        [filename: string]: {
            size: number;
            md5: string;
        };
    };
}
/**
 * md5文件名称
 */
const versionFileName = "version.txt"

/**
 * 项目目录
 */
const projectPath = Editor.Project.path;

/**
 * 发布目录名称
 */
const publishDirName = "publish"
/**
 * 发布目录
 */
const publishPath = path.join(projectPath, publishDirName);

/**
 * 远程主包配置文件
 */
const remoteConfigPath = `${publishPath}/remoteConfig`


/**
 * 子游戏版本目录名称
 */
const gameDir = "remote"

/**
 * 子游戏配置文件
 */
const subGameConfig = `${publishPath}/gameConfig`

/**
 * 当前平台  mac 发布ios windows 发布android
 */
const platform = myUtils.getPlatform();

if (!fs.existsSync(publishPath)) {
    fs.mkdirSync(publishPath);
}

module.exports = Editor.Panel.define({
    listeners: {
        show() { console.log('show'); },
        hide() { console.log('hide'); },
    },
    template: readFileSync(join(__dirname, '../../../static/template/default/index.html'), 'utf-8'),
    style: readFileSync(join(__dirname, '../../../static/style/default/index.css'), 'utf-8'),
    $: {
        app: '#app',
    },
    methods: {
        hello() {
            console.log('[cocos-panel-html.default]: hello');
        },
    },

    ready() {
        if (this.$.app) {
            const app = createApp({
                methods: {
                    /**
                     * 打开发布目录
                     */
                    openPublishDir() {
                        electron.shell.openPath(publishPath);
                    },
                    /**
                     * 构建按钮点击
                     */
                    onBuild() {
                        let exePath = path.join(Editor.App.path, '../../CocosCreator.exe');
                        let command = `${exePath} --project  ${Editor.Project.path} --build platform=${platform}`
                        // console.log(command)
                        let cmd = `start cmd /k "${command}`
                        ChildProcess.exec(cmd)
                    },
                    /**
                     * 发布按钮点击
                     */
                    onPublish() {
                        // console.log('根url===',this.rootUrl);
                        // console.log('是否增量更新===',this.incrementalUpdate)
                        // console.log('生成zip===',this.generateZip)
                        // console.log('发布过滤===',this.subGamePublish)
                        // console.log('选择所有子游戏===',this.selectAll)
                        //发布主包
                        if(this.rootUrl.length == 0){
                            Editor.Dialog.warn("请填写热更新根目录")
                            return
                        }
                        console.log('发布开始')
                        var mainVersion = myUtils.getSvnVersion(`${projectPath}/assets`)
                        this.publishMain(mainVersion);
                       
                        //发布子游戏
                        let gamesIDs = [] //保存要发布子游戏id
                        if (this.subGamePublish) {
                            for (const key in this.subGames) {
                                const b = this.subGames[key];
                                if (b) {
                                    this.publishSubGame(key,mainVersion);
                                    gamesIDs.push(key)
                                }
                            }
                        }

                         //生成zip
                         if (this.generateZip) {
                            //本次的版本文件夹
                            
                            var versionFolder = `${publishPath}/${platform}/version_${mainVersion}`
                            myUtils.zipFolder(versionFolder, undefined, `${versionFolder}.zip`)
                        }
                        //增量主版本
                        // console.log("增量主版本",this.incrementalUpdate)
                        // console.log("选择",this.incrementSelect)
                        if(this.incrementalUpdate){
                            var basePath = this.incrementOption[this.incrementSelect] //要对比的基础版本路径
                            this.buildIncremental(mainVersion,basePath,gamesIDs);
                        }
                        console.log('发布结束')
                    },
                    /**
                     * 增量更新
                     * @param mainVersion 
                     * @param basePath 
                     * @param gamesIDs
                     */
                    buildIncremental(mainVersion:number,basePath:string,gamesIDs:string[])
                    {
                        const regex = /version_(.*)/;
                        const match = regex.exec(basePath);

                        var baseVersion = "";
                        if(match)
                        {
                            baseVersion = match[1]; 
                        }
                        else
                        {
                            console.log("增量更新失败，未找到版本号")
                            return 
                        }
                            
                        var directoryPath = `${publishPath}/${platform}/version_${mainVersion}`

                        var newConfig = `${directoryPath}/${versionFileName}`//新版本配置
                        var oldConfig = `${basePath}/${versionFileName}`//旧版本配置
                        //旧数据
                        var oldConfigData:verisonConfig =  JSON.parse(fs.readFileSync(oldConfig, 'utf-8'))
                        //新数据
                        var newConfigData:verisonConfig =  JSON.parse(fs.readFileSync(newConfig, 'utf-8'))
                       
                        //判断新旧数据的差异文件
                        var diffFiles = []
                        for (const key in newConfigData.files) {
                            const newFile = newConfigData.files[key];
                            const oldFile = oldConfigData.files[key];
                            if(!oldFile || newFile.md5 != oldFile.md5)
                            {
                                // console.log("差异文件",key)
                                diffFiles.push(key)
                            }
                        }
                        //创建差异文件夹
                        var destDir = `${publishPath}/${platform}/version_${baseVersion}_${mainVersion}`
                        myUtils.mkdirSync(destDir)
                        //复制差异文件
                        // console.log("差异文件数量",diffFiles.length)
                        diffFiles.forEach((o)=>{
                            var src = path.join(directoryPath, o);
                            var target = path.join(destDir, o);
                            var temp = path.dirname(target)
                            myUtils.mkdirSync(temp)
                            fs.copyFileSync(src, target);
                        })

                        //复制最新配置文件
                        fs.copyFileSync(newConfig, `${destDir}/${versionFileName}`);
                        
                        //子游戏增量
                        if(gamesIDs.length > 0)
                        {
                            gamesIDs.forEach((game:string)=>{
                                this.buildSubGameIncremental(game,mainVersion,baseVersion)
                            })
                        }
                        //zip 
                        if (this.generateZip) {
                            myUtils.zipFolder(destDir, undefined, `${destDir}.zip`)
                        }

                        console.log(`增量完成=====》${baseVersion}_${mainVersion}`)   
                    },
                    /**
                     * 差异子游戏
                     * @param game 
                     * @param mainVersion 
                     * @param baseVersion 
                     */
                    buildSubGameIncremental(game:string,mainVersion:string,baseVersion:string)
                    {
                        var newGameDir = `${publishPath}/${platform}/version_${mainVersion}/${gameDir}/${game}`//新生成子游戏目录
                        var baseDir  = `${publishPath}/${platform}/version_${baseVersion}/${gameDir}/${game}` //基础子游戏目录

                        var newConfig = `${newGameDir}/${versionFileName}`//新版本配置
                        var oldConfig = `${baseDir}/${versionFileName}`//旧版本配置
                        //旧数据
                        var oldConfigData:verisonConfig =  JSON.parse(fs.readFileSync(oldConfig, 'utf-8'))
                        //新数据
                        var newConfigData:verisonConfig =  JSON.parse(fs.readFileSync(newConfig, 'utf-8'))

                        //判断新旧数据的差异文件
                        var diffFiles = []
                        for (const key in newConfigData.files) {
                            const newFile = newConfigData.files[key];
                            const oldFile = oldConfigData.files[key];
                            if(!oldFile || newFile.md5 != oldFile.md5)
                            {
                                // console.log("差异文件",key)
                                diffFiles.push(key)
                            }
                        }
                        //创建差异文件夹
                        var destDir = `${publishPath}/${platform}/version_${baseVersion}_${mainVersion}/${gameDir}/${game}`
                        myUtils.mkdirSync(destDir)
                        //复制差异文件
                        // console.log("差异文件数量",diffFiles.length)
                        let srcDir =  `${publishPath}/${platform}/version_${mainVersion}`
                        diffFiles.forEach((o)=>{
                            var src = path.join(srcDir, o);
                            var target = path.join(destDir, o);
                            var temp = path.dirname(target)
                            myUtils.mkdirSync(temp)
                            // console.log("复制文件",src,target)
                            fs.copyFileSync(src, target);
                        })
                        //复制最新配置文件
                        fs.copyFileSync(newConfig, `${destDir}/${versionFileName}`);
                    },
                    /**
                     * 发布主包
                     * @param scriptVersion
                     */
                    publishMain(scriptVersion:string): string {

                        let assetsRootPath = "";
                        if (platform == "android") {
                            assetsRootPath = path.join(projectPath, 'build/android/data');
                        }
                        else if (platform == "ios") {
                            assetsRootPath = path.join(projectPath, 'build/ios/data');
                        }

                        let versionData: {
                            scriptVersion: string;
                            files: {
                                [filename: string]: {
                                    size: number;
                                    md5: string;
                                };
                            };
                        } = {
                            scriptVersion: scriptVersion,
                            files: {}
                        }

                        // 构建后默认资源目录
                        var assetsPaths = ['src', 'assets'];
                        //本次的版本文件夹
                        var versionFolder = `${publishPath}/${platform}/version_${scriptVersion}`
                        myUtils.deleteFolderRecursive(versionFolder);
                        myUtils.mkdirSync(versionFolder)

                        //复制src assets 文件夹到本次版本文件夹
                        assetsPaths.forEach((o) => {
                            var src = path.join(assetsRootPath, o);
                            var target = path.join(versionFolder, o);
                            myUtils.copyFolderSync(src, target);
                        })

                        //把assets子文件夹下面的import文件夹压缩成zip
                        let assetsFolder = `${versionFolder}/assets`
                        var dirList = myUtils.getDirList(assetsFolder)
                        var verisonFilePath = `${Editor.Project.path}/assets/resources/${versionFileName}.meta`
                        let versionFileUUID = JSON.parse(fs.readFileSync(verisonFilePath).toString()).uuid
                        let tempMd5:{[key:string]:string} = {}
                        dirList.forEach((o) => {
                            var src = path.join(assetsFolder, `${o}/import`);
                            if (fs.existsSync(src)) {
                                const folderMd5 = myUtils.getFolderFilesMd5(src)
                                tempMd5[`${src}.zip`] = myUtils.getStringMD5(folderMd5)
                                myUtils.zipFolder(src, "import", `${src}.zip`,(fileName)=>{ return !(fileName.indexOf(versionFileUUID) >= 0)})
                                myUtils.deleteFolderRecursive(src);
                            }
                            src = path.join(assetsFolder, `${o}/native`);
                            if (fs.existsSync(src)) {
                                const folderMd5 = myUtils.getFolderFilesMd5(src)
                                tempMd5[`${src}.zip`] = myUtils.getStringMD5(folderMd5)
                                myUtils.zipFolder(src, "native", `${src}.zip`)
                                myUtils.deleteFolderRecursive(src);
                            }
                        })
                        //获取文件夹
                        var assetsList: { filePath: string, size: number }[] = [];
                        assetsPaths.forEach((o) => {
                            assetsList.push(...myUtils.listFiles(path.join(versionFolder, o)));
                        });

                        //填充files
                        assetsList.forEach((assetStat) => {
                            var md5 = ""
                            if(tempMd5[assetStat.filePath])
                            {
                                md5 = tempMd5[assetStat.filePath]
                            }
                            else
                            {
                                md5 =  myUtils.getFileMd5(assetStat.filePath)
                            }
                            var assetUrl = path.relative(assetsRootPath, assetStat.filePath);
                            assetUrl = assetUrl.replace(/\\/g, '/');
                            assetUrl = encodeURI(assetUrl);
                            assetUrl = assetUrl.replace(`../../../${publishDirName}/${platform}/version_${scriptVersion}/`, ``)

                            versionData.files[assetUrl] = {
                                size: assetStat.size,
                                md5: md5,
                            };
                        });

                        //写入本次的版本配置到版本文件夹
                        var verisonFile = `${versionFolder}/${versionFileName}`
                        myUtils.writeFile(verisonFile, JSON.stringify(versionData));

                        //复制当前版本到项目目录
                        var projectVersionFolder = `${projectPath}/assets/resources/${versionFileName}`
                        fs.copyFileSync(verisonFile, projectVersionFolder);


                        //写入最新的版本号到配置
                        let remoteData: remoteConfig = {
                            scriptVersion: scriptVersion,
                            baseUrl: this.rootUrl,
                            configFile: versionFileName,
                            channels: [],
                            supportBinarys: [],
                            forcedBinaryVersions: []
                        }
                        var lastData = myUtils.readFile(remoteConfigPath)
                        if (lastData) {
                            var data = JSON.parse(lastData) as remoteConfig;
                            remoteData.channels = data.channels;
                            remoteData.supportBinarys = data.supportBinarys;
                            remoteData.forcedBinaryVersions = data.forcedBinaryVersions;
                        }


                        myUtils.writeFile(remoteConfigPath, JSON.stringify(remoteData, null, 4));

                       

                        //版本文件同步到导出文件

                        if(fs.existsSync(verisonFilePath)){
                            var dst = `${Editor.Project.path}/build/${platform}/data/assets/resources`
                            const srcFiles = myUtils.listFiles(dst);
                            for (let i = srcFiles.length - 1; i >= 0; i--) {
                                var fileName = srcFiles[i].filePath;
                                if (fileName.indexOf(versionFileUUID) >= 0) {
                                    var oldData = JSON.parse(fs.readFileSync(fileName,'utf-8'))
                                    oldData[5][0][2] = JSON.stringify(versionData)
                                    fs.writeFileSync(fileName, JSON.stringify(oldData));

                                     //覆盖zip中的数据
                                     var zipPath = `${versionFolder}/assets/resources/import.zip`
                                     dst = path.join(dst, "/")
                                     var zipFile = fileName.replace(dst, "")
                                    //  console.log("zipPath",zipPath,zipFile)
                                     myUtils.addZipFile(zipPath, zipFile, JSON.stringify(oldData))
                                     break
                                }
                            }
                        }
                        
                        console.log("发布主包完成==============》", scriptVersion)
                        return scriptVersion
                    },
                    /**
                     * 发布子游戏
                     * @param game 
                     * @param mainVersion
                     */
                    publishSubGame(game: string,mainVersion:string) {

                        //版本生成目录
                        var versionFolder = `${publishPath}/${platform}/version_${mainVersion}/${gameDir}/${game}`
                        
                        // 获取子游戏版本号
                        var gameFloderPath = `${projectPath}/assets/Game/${game}`
                        var gameVersion = myUtils.getSvnVersion(gameFloderPath)
                        //导出子游戏目录
                        let gamePath = "";
                        let assetsRootPath = "";
                        if (platform == "android") {
                            gamePath = path.join(projectPath, `build/android/data/${gameDir}/${game}`);
                            assetsRootPath = path.join(projectPath, 'build/android/data');
                        }
                        else if (platform == "ios") {
                            gamePath = path.join(projectPath, `build/ios/data/${gameDir}/${game}`);
                            assetsRootPath = path.join(projectPath, 'build/ios/data');
                        }

                        
                        //删除文件夹
                        myUtils.deleteFolderRecursive(versionFolder);
                        myUtils.mkdirSync(versionFolder)

                        myUtils.copyFolderSync(gamePath, versionFolder);

                        //子游戏import文件夹 压缩成zip
                        var zipFolder = ["import", "native"]
                        let tempMd5:{[key:string]:string} = {}
                        zipFolder.forEach((o) => {
                            var src = path.join(versionFolder, o);
                            if (fs.existsSync(src)) {
                                const folderMd5 = myUtils.getFolderFilesMd5(src)
                                tempMd5[`${src}.zip`] = myUtils.getStringMD5(folderMd5)
                                myUtils.zipFolder(src, o, `${src}.zip`)
                                myUtils.deleteFolderRecursive(src);
                            }
                        })

                        let versionConfig: {
                            scriptVersion: string;
                            files: {
                                [filename: string]: {
                                    size: number;
                                    md5: string;
                                };
                            };
                        } = {
                            scriptVersion: gameVersion,
                            files: {}
                        }
                        //md5
                        var assetsList: { filePath: string, size: number }[] = myUtils.listFiles(versionFolder)
                        assetsList.forEach((assetStat) => {
                            var md5 = ""
                            if(tempMd5[assetStat.filePath])
                            {
                                md5 = tempMd5[assetStat.filePath]
                            }
                            else
                            {
                                md5 =  myUtils.getFileMd5(assetStat.filePath)
                            }
                            var assetUrl = path.relative(assetsRootPath, assetStat.filePath);
                            assetUrl = assetUrl.replace(/\\/g, '/');
                            assetUrl = encodeURI(assetUrl);
                            assetUrl = assetUrl.replace(`../../../${publishDirName}/${platform}/version_${mainVersion}/`, ``)

                            versionConfig.files[assetUrl] = {
                                size: assetStat.size,
                                md5: md5,
                            };
                        });

                        //写入本次的版本配置
                        myUtils.mkdirSync(versionFolder)
                        var verisonFile = `${versionFolder}/${versionFileName}`
                        myUtils.writeFile(verisonFile, JSON.stringify(versionConfig));

                        //写入最新的配置到子游戏配置
                        var subGameInfo: subGameConfig = {
                            baseUrl: this.rootUrl,
                            subgames: {}
                        }
                        var lastData = myUtils.readFile(subGameConfig)
                        if (lastData) {
                            subGameInfo.subgames = JSON.parse(lastData).subgames;
                        }

                        subGameInfo.subgames[game] = {
                            ID: parseInt(game),
                            version: gameVersion
                        }
                        myUtils.writeFile(subGameConfig, JSON.stringify(subGameInfo, null, 4));
                        console.log("发布子游戏完成===========》", game, gameVersion)
                    },
                    /**
                     * 是否全选子游戏
                     * @param event 
                     */
                    onSelectAll(event: any) {
                        for (const key in this.subGames) {
                            this.subGames[key] = this.selectAll;
                        }
                    },
                    /**
                     * 获取游戏列表
                     */
                    getGameList() {
                        const directoryPath = path.join(Editor.Project.path, 'assets/Game');
                        // 读取目录下所有文件和子目录
                        let filenames = fs.readdirSync(directoryPath)
                        const gameList: string[] = filenames.filter(file => {
                            return fs.statSync(path.join(directoryPath, file)).isDirectory() && /^\d+$/.test(file);
                        });
                        let data: { [key: string]: boolean } = {};
                        for (const key in gameList) {
                            var gameName = gameList[key];
                            data[gameName] = false;
                        }
                        return data;
                    },
                    /**
                * 获取增量选项
                * @returns 
                */
                    getIncrementOption(): string[] {
                        var directoryPath = `${publishPath}/${platform}`
                        let gameList:string[] =[]
                        if(fs.existsSync(directoryPath)){
                            let filenames = fs.readdirSync(directoryPath)
                            gameList = filenames.filter(file => {
                                return fs.statSync(path.join(directoryPath, file)).isDirectory() && /^version_(.*)$/.test(file);
                            });

                            for (const key in gameList) {
                                var pathDir = gameList[key];
                                gameList[key] = path.join(directoryPath, pathDir)
                            }
                        }
                        
                        return gameList;
                    },
                },

                /**
                 * 进入的时候初始化数据
                 */
                beforeMount() {
                    this.subGames = reactive(this.getGameList())
                    this.incrementOption = this.getIncrementOption();
                    const data = ConfigManager.Instance.getData();
                    this.rootUrl = data.rootUrl;
                    this.incrementalUpdate = data.incrementalUpdate;
                    this.generateZip = data.generateZip;
                    this.subGamePublish = data.subGamePublish;
                    this.selectAll = data.selectAll;
                    var subGames = data.subGames;
                    for (const key in subGames) {
                        const element = subGames[key];
                        this.subGames[key] = element;
                    }

                },
                /**
                 * 退出保存数据
                 */
                beforeUnmount() {
                    ConfigManager.Instance.saveData(this.$data);
                },
                data() {
                    return {
                        rootUrl: '', //热更新根目录
                        generateZip: false, //生成zip
                        incrementalUpdate: false, //增量更新
                        incrementOption: [], //增量更新选项
                        incrementSelect: ref(0),   //增量对应版本路径
                        subGamePublish: false, //子游戏发布过滤开关
                        selectAll: false, //全选子游戏
                        subGames: {}//子游戏列表    
                    }
                }
            });
            app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('ui-');
            app.mount(this.$.app);
            panelDataMap.set(this, app);
        }
    },
    beforeClose() { },
    close() {
        const app = panelDataMap.get(this);
        if (app) {
            app.unmount();
        }

    },
});
